package it.ap.sa.core.logback.config;

import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.Appender;
import it.ap.sa.core.configuration.commons.IConfiguration;
import it.ap.sa.core.configuration.iservice.IService;
import it.ap.sa.core.configuration.service.ConfigurationService;
import it.ap.sa.core.confservice.event.CoreServiceEventLoad;
import it.ap.sa.core.confservice.event.CoreServiceEventReload;
import it.ap.sa.core.utils.CoreProperties;
import it.ap.sa.core.utils.costants.AppenderTypeCostants;
import it.ap.sa.core.utils.costants.ReloadServiceConstants;


/**
 * @author Alessandro Paolillo
 */
@Service
public class LogbackService implements IService {
	
	private static final Logger logger = LoggerFactory.getLogger(LogbackService.class.getName());
	
	private LogbackService() {}

	@Autowired
	ApplicationEventPublisher publisher;

	@Autowired
	private CoreProperties property;
	
	/**
	 * Funzione che determina i logger da creare in base ai parametri di configurazione presenti nell'oggetto {@link IConfiguration}
	 * con prefix "log.".
	 * 
	 * @return {@link IAuditWriter}
	 */
	@SuppressWarnings("rawtypes")
	private void buildLogback(){
		try{
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Build logback logger ... IN CORSO <<<<<<<<<<<<<<<<< ");

			/**
			 * leggo dalla configurazione gli appender ed i logger da creare, 
			 */
			IConfiguration config = ConfigurationService.getConfig();
			
			Iterator<String> keysMatch = config.getKeys(property.prefixLogger);

			/** controllo che non è vuoto **/
			if(keysMatch != null && keysMatch.hasNext()){
				
				logger.info("Trovati dei logger tra i parametri di configurazione .. creazione logger start ... ");
				/** **/
				ObjectMapper mapper = new ObjectMapper();
					
				/** ciclo **/
				while (keysMatch.hasNext()) {
					// get key configuration
					String key = keysMatch.next();
					// get value {@link LogbackDTO} as json
					String value = config.getString(key);
					
					logger.debug(">>>>>>>>>>>>>>>>>>>>> Logback info: ");
					logger.debug("Key: " + key);
					logger.debug("Json: " + value);
					
					try{
						LogbackDTO logbackInfo = mapper.readValue(value, new TypeReference<LogbackDTO>() { });
						
						logger.info("Build appender >> " + logbackInfo.getTipo());
						
						Appender appender = null;
						
						// Console
						if(logbackInfo.getTipo().name().equals(AppenderTypeCostants.CONSOLE.name()))
							appender = LogbackBuilder.buildConsoleAppender(logbackInfo.getPattern(), logbackInfo.getNomeAppender());
						else
							// RollingFileAppender
							if(logbackInfo.getTipo().name().equals(AppenderTypeCostants.ROLLING_FILE.name()))
								appender = LogbackBuilder.buildRollingFileAppender(
												logbackInfo.getNomeAppender(), logbackInfo.getLocateFile(), logbackInfo.getNomeFile(), logbackInfo.getFilenamePattern(), logbackInfo.getMaxFileSize(), logbackInfo.getPattern());
						
						// build logger
						LogbackBuilder.buildLogger(logbackInfo.getNomeLogger(), appender, logbackInfo.getLevel(), logbackInfo.getAdditivity());
						
					}catch(Exception e){
						logger.error("Build del logger/appender non riuscito!! .. continue anyway " + e, e);
					}
				}// close while
			}else{
				/**
				 * Nelle configurazione l'elenco logger è vuoto, uso l'impostazione di default.
				 */
				logger.info(">>>>>>>>>>>>>>> Nessun logger trovato nelle configuration ... uso il default <<<<<<<<<<<<<<<<<");
				
				/**
				 * Costuisco i logger di default per l'applicazione
				 */
				buildDefaultLoggerApplication();
			}
			
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Logback logger builded ... TERMINATO <<<<<<<<<<<<<<<<< ");
		}catch(Exception e){
			logger.error("Error load logback logger ... build default logger " + e, e);
			
			/**
			 * Costuisco i logger di default per l'applicazione
			 */
			buildDefaultLoggerApplication();
		}
	}
	
	/**
	 * Costuisco i logger di default per l'applicazione
	 */
	@SuppressWarnings("rawtypes")
	private void buildDefaultLoggerApplication() {
		try{
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Build default logger ... IN CORSO <<<<<<<<<<<<<<<<< ");
			
			Appender consoleAppender = LogbackBuilder.buildConsoleAppender(property.logback_console_pattern, property.logback_console_appender);
			
			LogbackBuilder.buildLogger(Logger.ROOT_LOGGER_NAME, consoleAppender, property.logback_console_level, true);
			
			LogbackBuilder.buildLogger("it", consoleAppender, Level.INFO.toString(), false);
			
			LogbackBuilder.buildLogger("org", consoleAppender, Level.WARN.toString(), false);
			
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Build default logger ... TERMINATO <<<<<<<<<<<<<<<<< ");
		}catch(Exception e){
			logger.error("Error build logger di default dell'applicazione ... " + e, e);

			throw new RuntimeException("Error build logger di default dell'applicazione");
		}
	}

	/**
	 * @author Alessandro Paolillo
	 */
	@EventListener
	public void onServiceEventLoad(CoreServiceEventLoad event) {
		try{
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Starting logback service ... IN CORSO <<<<<<<<<<<<<<<<< ");

			/**
			 * Creazione logger
			 */
			buildLogback();
			
			logger.info(">>>>>>>>>>>>>>>>>>>>>>> Logback service started ... TERMINATO <<<<<<<<<<<<<<<<< ");
		}catch(Exception e){
			logger.error("Error load logback service ... build default logger " + e, e);
			
			/**
			 * Costuisco i logger di default per l'applicazione
			 */
			buildDefaultLoggerApplication();
		}
	}
	/**
	 * @author Alessandro Paolillo
	 */
	@EventListener
	public void onReloadConfigEvent(CoreServiceEventReload event) {
		try{
			if(event.getReloadService().name().equals(ReloadServiceConstants.LOGBACK.name())){
				logger.info(">>>>>>>>>>>>>>>>>>>>>>> Reload logback service ... IN CORSO <<<<<<<<<<<<<<<<< ");
				
				LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
				
				// remove all appender
				loggerContext.reset();
				
				/**
				 * Ridefinisco i logger e gli appender.
				 */
				buildLogback();
				
				logger.info(">>>>>>>>>>>>>>>>>>>>>>> Logback service restarted... TERMINATO <<<<<<<<<<<<<<<<< ");
			}
		}catch(Exception e){
			logger.error("Error reload logback service ... build default logger " + e, e);
			
			/**
			 * Costuisco i logger di default per l'applicazione
			 */
			buildDefaultLoggerApplication();
		}
	}
}
